/**
 * Copyright (C) 2007-2010 SlimDX Group
 *
 * Permission is hereby granted, free  of charge, to any person obtaining a copy of this software  and
 * associated  documentation  files (the  "Software"), to deal  in the Software  without  restriction,
 * including  without  limitation  the  rights  to use,  copy,  modify,  merge,  publish,  distribute,
 * sublicense, and/or sell  copies of the  Software,  and to permit  persons to whom  the Software  is
 * furnished to do so, subject to the following conditions:
 *
 * The  above  copyright  notice  and this  permission  notice shall  be included  in  all  copies  or
 * substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS",  WITHOUT WARRANTY OF  ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
 * NOT  LIMITED  TO  THE  WARRANTIES  OF  MERCHANTABILITY,  FITNESS  FOR  A   PARTICULAR  PURPOSE  AND
 * NONINFRINGEMENT.  IN  NO  EVENT SHALL THE  AUTHORS  OR COPYRIGHT HOLDERS  BE LIABLE FOR  ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,  OUT
 * OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include "Matrix.hpp"
#include "Vector3.hpp"
#include "Quaternion.hpp"

namespace GTA
{
	namespace Math
	{
		using namespace System;
		using namespace System::Globalization;

		float Matrix::default::get(int row, int column)
		{
			if (row < 0 || row > 3)
				throw gcnew ArgumentOutOfRangeException("row", "Rows and columns for matrices run from 0 to 3, inclusive.");

			if (column < 0 || column > 3)
				throw gcnew ArgumentOutOfRangeException("column", "Rows and columns for matrices run from 0 to 3, inclusive.");

			int index = row * 4 + column;

			switch (index)
			{
				case 0:  return M11;
				case 1:  return M12;
				case 2:  return M13;
				case 3:  return M14;
				case 4:  return M21;
				case 5:  return M22;
				case 6:  return M23;
				case 7:  return M24;
				case 8:  return M31;
				case 9:  return M32;
				case 10: return M33;
				case 11: return M34;
				case 12: return M41;
				case 13: return M42;
				case 14: return M43;
				case 15: return M44;
			}

			return 0.0f;
		}
		void Matrix::default::set(int row, int column, float value)
		{
			if (row < 0 || row > 3)
				throw gcnew ArgumentOutOfRangeException("row", "Rows and columns for matrices run from 0 to 3, inclusive.");

			if (column < 0 || column > 3)
				throw gcnew ArgumentOutOfRangeException("column", "Rows and columns for matrices run from 0 to 3, inclusive.");

			int index = row * 4 + column;
			switch (index)
			{
				case 0:  M11 = value; break;
				case 1:  M12 = value; break;
				case 2:  M13 = value; break;
				case 3:  M14 = value; break;
				case 4:  M21 = value; break;
				case 5:  M22 = value; break;
				case 6:  M23 = value; break;
				case 7:  M24 = value; break;
				case 8:  M31 = value; break;
				case 9:  M32 = value; break;
				case 10: M33 = value; break;
				case 11: M34 = value; break;
				case 12: M41 = value; break;
				case 13: M42 = value; break;
				case 14: M43 = value; break;
				case 15: M44 = value; break;
			}
		}

		bool Matrix::IsIdentity::get()
		{
			if (M11 != 1.0f || M22 != 1.0f || M33 != 1.0f || M44 != 1.0f)
				return false;

			if (M12 != 0.0f || M13 != 0.0f || M14 != 0.0f ||
				M21 != 0.0f || M23 != 0.0f || M24 != 0.0f ||
				M31 != 0.0f || M32 != 0.0f || M34 != 0.0f ||
				M41 != 0.0f || M42 != 0.0f || M43 != 0.0f)
				return false;

			return true;
		}
		bool Matrix::HasInverse::get()
		{
			return Determinant() != 0.0f;
		}

		Matrix Matrix::FromArray(array<float> ^floatArray)
		{
			if (floatArray->Length != 16)
				throw gcnew Exception("Array must contain 16 items to be converted to a 4*4 Matrix");
			Matrix result;
			result.M11 = floatArray[0];
			result.M12 = floatArray[1];
			result.M13 = floatArray[2];
			result.M14 = floatArray[3];

			result.M21 = floatArray[4];
			result.M22 = floatArray[5];
			result.M23 = floatArray[6];
			result.M24 = floatArray[7];

			result.M31 = floatArray[8];
			result.M32 = floatArray[9];
			result.M33 = floatArray[10];
			result.M34 = floatArray[11];

			result.M41 = floatArray[12];
			result.M42 = floatArray[13];
			result.M43 = floatArray[14];
			result.M44 = floatArray[15];

			return result;
		}

		float Matrix::Determinant()
		{
			float temp1 = (M33 * M44) - (M34 * M43);
			float temp2 = (M32 * M44) - (M34 * M42);
			float temp3 = (M32 * M43) - (M33 * M42);
			float temp4 = (M31 * M44) - (M34 * M41);
			float temp5 = (M31 * M43) - (M33 * M41);
			float temp6 = (M31 * M42) - (M32 * M41);

			return ((((M11 * (((M22 * temp1) - (M23 * temp2)) + (M24 * temp3))) - (M12 * (((M21 * temp1) - (M23 * temp4)) + (M24 * temp5)))) + (M13 * (((M21 * temp2) - (M22 * temp4)) + (M24 * temp6)))) - (M14 * (((M21 * temp3) - (M22 * temp5)) + (M23 * temp6))));
		}
		float Det3x3(float M11, float M12, float M13, float M21, float M22, float M23, float M31, float M32, float M33)
		{
			return M11 * (M22 * M33 - M23 * M32) - M12 * (M21 * M33 - M23 * M31) + M13 * (M21 * M32 - M22 * M31);
		}

		void Matrix::Inverse()
		{
			float Det = Determinant();
			if (Det == 0.0f)
				return;
			float tM11 = Det3x3(M22, M23, M24, M32, M33, M34, M42, M43, M44) / Det;
			float tM21 = -Det3x3(M21, M23, M24, M31, M33, M34, M41, M43, M44) / Det;
			float tM31 = Det3x3(M21, M22, M24, M31, M32, M34, M41, M42, M44) / Det;
			float tM41 = -Det3x3(M21, M22, M23, M31, M32, M33, M41, M42, M43) / Det;

			float tM12 = -Det3x3(M12, M13, M14, M32, M33, M34, M42, M43, M44) / Det;
			float tM22 = Det3x3(M11, M13, M14, M31, M33, M34, M41, M43, M44) / Det;
			float tM32 = -Det3x3(M11, M12, M14, M31, M32, M34, M41, M42, M44) / Det;
			float tM42 = Det3x3(M11, M12, M13, M31, M32, M33, M41, M42, M43) / Det;

			float tM13 = Det3x3(M12, M13, M14, M22, M23, M24, M42, M43, M44) / Det;
			float tM23 = -Det3x3(M11, M13, M14, M21, M23, M24, M41, M43, M44) / Det;
			float tM33 = Det3x3(M11, M12, M14, M21, M22, M24, M41, M42, M44) / Det;
			float tM43 = -Det3x3(M11, M12, M13, M21, M22, M23, M41, M42, M43) / Det;

			float tM14 = -Det3x3(M12, M13, M14, M22, M23, M24, M32, M33, M34) / Det;
			float tM24 = Det3x3(M11, M13, M14, M21, M23, M24, M31, M33, M34) / Det;
			float tM34 = -Det3x3(M11, M12, M14, M21, M22, M24, M31, M32, M34) / Det;
			float tM44 = Det3x3(M11, M12, M13, M21, M22, M23, M31, M32, M33) / Det;

			M11 = tM11;
			M12 = tM12;
			M13 = tM13;
			M14 = tM14;

			M21 = tM21;
			M22 = tM22;
			M23 = tM23;
			M24 = tM24;

			M31 = tM31;
			M32 = tM32;
			M33 = tM33;
			M34 = tM34;

			M41 = tM41;
			M42 = tM42;
			M43 = tM43;
			M44 = tM44;
		}

		Matrix Matrix::Add(Matrix left, Matrix right)
		{
			Matrix result;
			result.M11 = left.M11 + right.M11;
			result.M12 = left.M12 + right.M12;
			result.M13 = left.M13 + right.M13;
			result.M14 = left.M14 + right.M14;
			result.M21 = left.M21 + right.M21;
			result.M22 = left.M22 + right.M22;
			result.M23 = left.M23 + right.M23;
			result.M24 = left.M24 + right.M24;
			result.M31 = left.M31 + right.M31;
			result.M32 = left.M32 + right.M32;
			result.M33 = left.M33 + right.M33;
			result.M34 = left.M34 + right.M34;
			result.M41 = left.M41 + right.M41;
			result.M42 = left.M42 + right.M42;
			result.M43 = left.M43 + right.M43;
			result.M44 = left.M44 + right.M44;
			return result;
		}
		Matrix Matrix::Subtract(Matrix left, Matrix right)
		{
			Matrix result;
			result.M11 = left.M11 - right.M11;
			result.M12 = left.M12 - right.M12;
			result.M13 = left.M13 - right.M13;
			result.M14 = left.M14 - right.M14;
			result.M21 = left.M21 - right.M21;
			result.M22 = left.M22 - right.M22;
			result.M23 = left.M23 - right.M23;
			result.M24 = left.M24 - right.M24;
			result.M31 = left.M31 - right.M31;
			result.M32 = left.M32 - right.M32;
			result.M33 = left.M33 - right.M33;
			result.M34 = left.M34 - right.M34;
			result.M41 = left.M41 - right.M41;
			result.M42 = left.M42 - right.M42;
			result.M43 = left.M43 - right.M43;
			result.M44 = left.M44 - right.M44;
			return result;
		}
		Matrix Matrix::Negate(Matrix matrix)
		{
			Matrix result;
			result.M11 = -matrix.M11;
			result.M12 = -matrix.M12;
			result.M13 = -matrix.M13;
			result.M14 = -matrix.M14;
			result.M21 = -matrix.M21;
			result.M22 = -matrix.M22;
			result.M23 = -matrix.M23;
			result.M24 = -matrix.M24;
			result.M31 = -matrix.M31;
			result.M32 = -matrix.M32;
			result.M33 = -matrix.M33;
			result.M34 = -matrix.M34;
			result.M41 = -matrix.M41;
			result.M42 = -matrix.M42;
			result.M43 = -matrix.M43;
			result.M44 = -matrix.M44;
			return result;
		}

		Matrix Matrix::Inverse(Matrix matrix)
		{
			matrix.Inverse();
			return matrix;
		}
		Matrix Matrix::Multiply(Matrix left, Matrix right)
		{
			Matrix result;
			result.M11 = (left.M11 * right.M11) + (left.M12 * right.M21) + (left.M13 * right.M31) + (left.M14 * right.M41);
			result.M12 = (left.M11 * right.M12) + (left.M12 * right.M22) + (left.M13 * right.M32) + (left.M14 * right.M42);
			result.M13 = (left.M11 * right.M13) + (left.M12 * right.M23) + (left.M13 * right.M33) + (left.M14 * right.M43);
			result.M14 = (left.M11 * right.M14) + (left.M12 * right.M24) + (left.M13 * right.M34) + (left.M14 * right.M44);
			result.M21 = (left.M21 * right.M11) + (left.M22 * right.M21) + (left.M23 * right.M31) + (left.M24 * right.M41);
			result.M22 = (left.M21 * right.M12) + (left.M22 * right.M22) + (left.M23 * right.M32) + (left.M24 * right.M42);
			result.M23 = (left.M21 * right.M13) + (left.M22 * right.M23) + (left.M23 * right.M33) + (left.M24 * right.M43);
			result.M24 = (left.M21 * right.M14) + (left.M22 * right.M24) + (left.M23 * right.M34) + (left.M24 * right.M44);
			result.M31 = (left.M31 * right.M11) + (left.M32 * right.M21) + (left.M33 * right.M31) + (left.M34 * right.M41);
			result.M32 = (left.M31 * right.M12) + (left.M32 * right.M22) + (left.M33 * right.M32) + (left.M34 * right.M42);
			result.M33 = (left.M31 * right.M13) + (left.M32 * right.M23) + (left.M33 * right.M33) + (left.M34 * right.M43);
			result.M34 = (left.M31 * right.M14) + (left.M32 * right.M24) + (left.M33 * right.M34) + (left.M34 * right.M44);
			result.M41 = (left.M41 * right.M11) + (left.M42 * right.M21) + (left.M43 * right.M31) + (left.M44 * right.M41);
			result.M42 = (left.M41 * right.M12) + (left.M42 * right.M22) + (left.M43 * right.M32) + (left.M44 * right.M42);
			result.M43 = (left.M41 * right.M13) + (left.M42 * right.M23) + (left.M43 * right.M33) + (left.M44 * right.M43);
			result.M44 = (left.M41 * right.M14) + (left.M42 * right.M24) + (left.M43 * right.M34) + (left.M44 * right.M44);
			return result;
		}
		Matrix Matrix::Multiply(Matrix left, float right)
		{
			Matrix result;
			result.M11 = left.M11 * right;
			result.M12 = left.M12 * right;
			result.M13 = left.M13 * right;
			result.M14 = left.M14 * right;
			result.M21 = left.M21 * right;
			result.M22 = left.M22 * right;
			result.M23 = left.M23 * right;
			result.M24 = left.M24 * right;
			result.M31 = left.M31 * right;
			result.M32 = left.M32 * right;
			result.M33 = left.M33 * right;
			result.M34 = left.M34 * right;
			result.M41 = left.M41 * right;
			result.M42 = left.M42 * right;
			result.M43 = left.M43 * right;
			result.M44 = left.M44 * right;
			return result;
		}
		Matrix Matrix::Divide(Matrix left, Matrix right)
		{
			Matrix result;
			result.M11 = left.M11 / right.M11;
			result.M12 = left.M12 / right.M12;
			result.M13 = left.M13 / right.M13;
			result.M14 = left.M14 / right.M14;
			result.M21 = left.M21 / right.M21;
			result.M22 = left.M22 / right.M22;
			result.M23 = left.M23 / right.M23;
			result.M24 = left.M24 / right.M24;
			result.M31 = left.M31 / right.M31;
			result.M32 = left.M32 / right.M32;
			result.M33 = left.M33 / right.M33;
			result.M34 = left.M34 / right.M34;
			result.M41 = left.M41 / right.M41;
			result.M42 = left.M42 / right.M42;
			result.M43 = left.M43 / right.M43;
			result.M44 = left.M44 / right.M44;
			return result;
		}
		Matrix Matrix::Divide(Matrix left, float right)
		{
			Matrix result;
			float inv = 1.0f / right;

			result.M11 = left.M11 * inv;
			result.M12 = left.M12 * inv;
			result.M13 = left.M13 * inv;
			result.M14 = left.M14 * inv;
			result.M21 = left.M21 * inv;
			result.M22 = left.M22 * inv;
			result.M23 = left.M23 * inv;
			result.M24 = left.M24 * inv;
			result.M31 = left.M31 * inv;
			result.M32 = left.M32 * inv;
			result.M33 = left.M33 * inv;
			result.M34 = left.M34 * inv;
			result.M41 = left.M41 * inv;
			result.M42 = left.M42 * inv;
			result.M43 = left.M43 * inv;
			result.M44 = left.M44 * inv;
			return result;
		}
		Matrix Matrix::Lerp(Matrix value1, Matrix value2, float amount)
		{
			Matrix result;
			result.M11 = value1.M11 + ((value2.M11 - value1.M11) * amount);
			result.M12 = value1.M12 + ((value2.M12 - value1.M12) * amount);
			result.M13 = value1.M13 + ((value2.M13 - value1.M13) * amount);
			result.M14 = value1.M14 + ((value2.M14 - value1.M14) * amount);
			result.M21 = value1.M21 + ((value2.M21 - value1.M21) * amount);
			result.M22 = value1.M22 + ((value2.M22 - value1.M22) * amount);
			result.M23 = value1.M23 + ((value2.M23 - value1.M23) * amount);
			result.M24 = value1.M24 + ((value2.M24 - value1.M24) * amount);
			result.M31 = value1.M31 + ((value2.M31 - value1.M31) * amount);
			result.M32 = value1.M32 + ((value2.M32 - value1.M32) * amount);
			result.M33 = value1.M33 + ((value2.M33 - value1.M33) * amount);
			result.M34 = value1.M34 + ((value2.M34 - value1.M34) * amount);
			result.M41 = value1.M41 + ((value2.M41 - value1.M41) * amount);
			result.M42 = value1.M42 + ((value2.M42 - value1.M42) * amount);
			result.M43 = value1.M43 + ((value2.M43 - value1.M43) * amount);
			result.M44 = value1.M44 + ((value2.M44 - value1.M44) * amount);
			return result;
		}
		Matrix Matrix::RotationX(float angle)
		{
			Matrix result;
			float cos = static_cast<float>(System::Math::Cos(static_cast<double>(angle)));
			float sin = static_cast<float>(System::Math::Sin(static_cast<double>(angle)));

			result.M11 = 1.0f;
			result.M12 = 0.0f;
			result.M13 = 0.0f;
			result.M14 = 0.0f;
			result.M21 = 0.0f;
			result.M22 = cos;
			result.M23 = sin;
			result.M24 = 0.0f;
			result.M31 = 0.0f;
			result.M32 = -sin;
			result.M33 = cos;
			result.M34 = 0.0f;
			result.M41 = 0.0f;
			result.M42 = 0.0f;
			result.M43 = 0.0f;
			result.M44 = 1.0f;

			return result;
		}
		Matrix Matrix::RotationY(float angle)
		{
			Matrix result;
			float cos = static_cast<float>(System::Math::Cos(static_cast<double>(angle)));
			float sin = static_cast<float>(System::Math::Sin(static_cast<double>(angle)));

			result.M11 = cos;
			result.M12 = 0.0f;
			result.M13 = -sin;
			result.M14 = 0.0f;
			result.M21 = 0.0f;
			result.M22 = 1.0f;
			result.M23 = 0.0f;
			result.M24 = 0.0f;
			result.M31 = sin;
			result.M32 = 0.0f;
			result.M33 = cos;
			result.M34 = 0.0f;
			result.M41 = 0.0f;
			result.M42 = 0.0f;
			result.M43 = 0.0f;
			result.M44 = 1.0f;

			return result;
		}
		Matrix Matrix::RotationZ(float angle)
		{
			Matrix result;
			float cos = static_cast<float>(System::Math::Cos(static_cast<double>(angle)));
			float sin = static_cast<float>(System::Math::Sin(static_cast<double>(angle)));

			result.M11 = cos;
			result.M12 = sin;
			result.M13 = 0.0f;
			result.M14 = 0.0f;
			result.M21 = -sin;
			result.M22 = cos;
			result.M23 = 0.0f;
			result.M24 = 0.0f;
			result.M31 = 0.0f;
			result.M32 = 0.0f;
			result.M33 = 1.0f;
			result.M34 = 0.0f;
			result.M41 = 0.0f;
			result.M42 = 0.0f;
			result.M43 = 0.0f;
			result.M44 = 1.0f;

			return result;
		}
		Matrix Matrix::RotationQuaternion(Quaternion quaternion)
		{
			Matrix result;

			float xx = quaternion.X * quaternion.X;
			float yy = quaternion.Y * quaternion.Y;
			float zz = quaternion.Z * quaternion.Z;
			float xy = quaternion.X * quaternion.Y;
			float zw = quaternion.Z * quaternion.W;
			float zx = quaternion.Z * quaternion.X;
			float yw = quaternion.Y * quaternion.W;
			float yz = quaternion.Y * quaternion.Z;
			float xw = quaternion.X * quaternion.W;
			result.M11 = 1.0f - (2.0f * (yy + zz));
			result.M12 = 2.0f * (xy + zw);
			result.M13 = 2.0f * (zx - yw);
			result.M14 = 0.0f;
			result.M21 = 2.0f * (xy - zw);
			result.M22 = 1.0f - (2.0f * (zz + xx));
			result.M23 = 2.0f * (yz + xw);
			result.M24 = 0.0f;
			result.M31 = 2.0f * (zx + yw);
			result.M32 = 2.0f * (yz - xw);
			result.M33 = 1.0f - (2.0f * (yy + xx));
			result.M34 = 0.0f;
			result.M41 = 0.0f;
			result.M42 = 0.0f;
			result.M43 = 0.0f;
			result.M44 = 1.0f;

			return result;
		}
		Matrix Matrix::RotationAxis(Vector3 axis, float angle)
		{
			if (axis.LengthSquared() != 1.0f)
				axis.Normalize();

			Matrix result;
			float x = axis.X;
			float y = axis.Y;
			float z = axis.Z;
			float cos = static_cast<float>(System::Math::Cos(static_cast<double>(angle)));
			float sin = static_cast<float>(System::Math::Sin(static_cast<double>(angle)));
			float xx = x * x;
			float yy = y * y;
			float zz = z * z;
			float xy = x * y;
			float xz = x * z;
			float yz = y * z;

			result.M11 = xx + (cos * (1.0f - xx));
			result.M12 = (xy - (cos * xy)) + (sin * z);
			result.M13 = (xz - (cos * xz)) - (sin * y);
			result.M14 = 0.0f;
			result.M21 = (xy - (cos * xy)) - (sin * z);
			result.M22 = yy + (cos * (1.0f - yy));
			result.M23 = (yz - (cos * yz)) + (sin * x);
			result.M24 = 0.0f;
			result.M31 = (xz - (cos * xz)) + (sin * y);
			result.M32 = (yz - (cos * yz)) - (sin * x);
			result.M33 = zz + (cos * (1.0f - zz));
			result.M34 = 0.0f;
			result.M41 = 0.0f;
			result.M42 = 0.0f;
			result.M43 = 0.0f;
			result.M44 = 1.0f;

			return result;
		}
		Matrix Matrix::RotationYawPitchRoll(float yaw, float pitch, float roll)
		{
			Quaternion quaternion = Quaternion::RotationYawPitchRoll(yaw, pitch, roll);
			return RotationQuaternion(quaternion);
		}
		Matrix Matrix::Translation(float x, float y, float z)
		{
			Matrix result;
			result.M11 = 1.0f;
			result.M12 = 0.0f;
			result.M13 = 0.0f;
			result.M14 = 0.0f;
			result.M21 = 0.0f;
			result.M22 = 1.0f;
			result.M23 = 0.0f;
			result.M24 = 0.0f;
			result.M31 = 0.0f;
			result.M32 = 0.0f;
			result.M33 = 1.0f;
			result.M34 = 0.0f;
			result.M41 = x;
			result.M42 = y;
			result.M43 = z;
			result.M44 = 1.0f;
			return result;
		}
		Matrix Matrix::Translation(Vector3 translation)
		{
			Matrix result;
			result.M11 = 1.0f;
			result.M12 = 0.0f;
			result.M13 = 0.0f;
			result.M14 = 0.0f;
			result.M21 = 0.0f;
			result.M22 = 1.0f;
			result.M23 = 0.0f;
			result.M24 = 0.0f;
			result.M31 = 0.0f;
			result.M32 = 0.0f;
			result.M33 = 1.0f;
			result.M34 = 0.0f;
			result.M41 = translation.X;
			result.M42 = translation.Y;
			result.M43 = translation.Z;
			result.M44 = 1.0f;
			return result;
		}
		Matrix Matrix::Scaling(float x, float y, float z)
		{
			Matrix result;
			result.M11 = x;
			result.M12 = 0.0f;
			result.M13 = 0.0f;
			result.M14 = 0.0f;
			result.M21 = 0.0f;
			result.M22 = y;
			result.M23 = 0.0f;
			result.M24 = 0.0f;
			result.M31 = 0.0f;
			result.M32 = 0.0f;
			result.M33 = z;
			result.M34 = 0.0f;
			result.M41 = 0.0f;
			result.M42 = 0.0f;
			result.M43 = 0.0f;
			result.M44 = 1.0f;
			return result;
		}
		Matrix Matrix::Scaling(Vector3 scaling)
		{
			Matrix result;
			result.M11 = scaling.X;
			result.M12 = 0.0f;
			result.M13 = 0.0f;
			result.M14 = 0.0f;
			result.M21 = 0.0f;
			result.M22 = scaling.Y;
			result.M23 = 0.0f;
			result.M24 = 0.0f;
			result.M31 = 0.0f;
			result.M32 = 0.0f;
			result.M33 = scaling.Z;
			result.M34 = 0.0f;
			result.M41 = 0.0f;
			result.M42 = 0.0f;
			result.M43 = 0.0f;
			result.M44 = 1.0f;
			return result;
		}
		Matrix Matrix::Transpose(Matrix mat)
		{
			Matrix result;
			result.M11 = mat.M11;
			result.M12 = mat.M21;
			result.M13 = mat.M31;
			result.M14 = mat.M41;
			result.M21 = mat.M12;
			result.M22 = mat.M22;
			result.M23 = mat.M32;
			result.M24 = mat.M42;
			result.M31 = mat.M13;
			result.M32 = mat.M23;
			result.M33 = mat.M33;
			result.M34 = mat.M43;
			result.M41 = mat.M14;
			result.M42 = mat.M24;
			result.M43 = mat.M34;
			result.M44 = mat.M44;
			return result;
		}

		Matrix Matrix::operator * (Matrix left, Matrix right)
		{
			Matrix result;
			result.M11 = (left.M11 * right.M11) + (left.M12 * right.M21) + (left.M13 * right.M31) + (left.M14 * right.M41);
			result.M12 = (left.M11 * right.M12) + (left.M12 * right.M22) + (left.M13 * right.M32) + (left.M14 * right.M42);
			result.M13 = (left.M11 * right.M13) + (left.M12 * right.M23) + (left.M13 * right.M33) + (left.M14 * right.M43);
			result.M14 = (left.M11 * right.M14) + (left.M12 * right.M24) + (left.M13 * right.M34) + (left.M14 * right.M44);
			result.M21 = (left.M21 * right.M11) + (left.M22 * right.M21) + (left.M23 * right.M31) + (left.M24 * right.M41);
			result.M22 = (left.M21 * right.M12) + (left.M22 * right.M22) + (left.M23 * right.M32) + (left.M24 * right.M42);
			result.M23 = (left.M21 * right.M13) + (left.M22 * right.M23) + (left.M23 * right.M33) + (left.M24 * right.M43);
			result.M24 = (left.M21 * right.M14) + (left.M22 * right.M24) + (left.M23 * right.M34) + (left.M24 * right.M44);
			result.M31 = (left.M31 * right.M11) + (left.M32 * right.M21) + (left.M33 * right.M31) + (left.M34 * right.M41);
			result.M32 = (left.M31 * right.M12) + (left.M32 * right.M22) + (left.M33 * right.M32) + (left.M34 * right.M42);
			result.M33 = (left.M31 * right.M13) + (left.M32 * right.M23) + (left.M33 * right.M33) + (left.M34 * right.M43);
			result.M34 = (left.M31 * right.M14) + (left.M32 * right.M24) + (left.M33 * right.M34) + (left.M34 * right.M44);
			result.M41 = (left.M41 * right.M11) + (left.M42 * right.M21) + (left.M43 * right.M31) + (left.M44 * right.M41);
			result.M42 = (left.M41 * right.M12) + (left.M42 * right.M22) + (left.M43 * right.M32) + (left.M44 * right.M42);
			result.M43 = (left.M41 * right.M13) + (left.M42 * right.M23) + (left.M43 * right.M33) + (left.M44 * right.M43);
			result.M44 = (left.M41 * right.M14) + (left.M42 * right.M24) + (left.M43 * right.M34) + (left.M44 * right.M44);
			return result;
		}
		Matrix Matrix::operator * (Matrix left, float right)
		{
			Matrix result;
			result.M11 = left.M11 * right;
			result.M12 = left.M12 * right;
			result.M13 = left.M13 * right;
			result.M14 = left.M14 * right;
			result.M21 = left.M21 * right;
			result.M22 = left.M22 * right;
			result.M23 = left.M23 * right;
			result.M24 = left.M24 * right;
			result.M31 = left.M31 * right;
			result.M32 = left.M32 * right;
			result.M33 = left.M33 * right;
			result.M34 = left.M34 * right;
			result.M41 = left.M41 * right;
			result.M42 = left.M42 * right;
			result.M43 = left.M43 * right;
			result.M44 = left.M44 * right;
			return result;
		}
		Matrix Matrix::operator * (float right, Matrix left)
		{
			return left * right;
		}
		Matrix Matrix::operator / (Matrix left, Matrix right)
		{
			Matrix result;
			result.M11 = left.M11 / right.M11;
			result.M12 = left.M12 / right.M12;
			result.M13 = left.M13 / right.M13;
			result.M14 = left.M14 / right.M14;
			result.M21 = left.M21 / right.M21;
			result.M22 = left.M22 / right.M22;
			result.M23 = left.M23 / right.M23;
			result.M24 = left.M24 / right.M24;
			result.M31 = left.M31 / right.M31;
			result.M32 = left.M32 / right.M32;
			result.M33 = left.M33 / right.M33;
			result.M34 = left.M34 / right.M34;
			result.M41 = left.M41 / right.M41;
			result.M42 = left.M42 / right.M42;
			result.M43 = left.M43 / right.M43;
			result.M44 = left.M44 / right.M44;
			return result;
		}
		Matrix Matrix::operator / (Matrix left, float right)
		{
			Matrix result;
			result.M11 = left.M11 / right;
			result.M12 = left.M12 / right;
			result.M13 = left.M13 / right;
			result.M14 = left.M14 / right;
			result.M21 = left.M21 / right;
			result.M22 = left.M22 / right;
			result.M23 = left.M23 / right;
			result.M24 = left.M24 / right;
			result.M31 = left.M31 / right;
			result.M32 = left.M32 / right;
			result.M33 = left.M33 / right;
			result.M34 = left.M34 / right;
			result.M41 = left.M41 / right;
			result.M42 = left.M42 / right;
			result.M43 = left.M43 / right;
			result.M44 = left.M44 / right;
			return result;
		}
		Matrix Matrix::operator + (Matrix left, Matrix right)
		{
			Matrix result;
			result.M11 = left.M11 + right.M11;
			result.M12 = left.M12 + right.M12;
			result.M13 = left.M13 + right.M13;
			result.M14 = left.M14 + right.M14;
			result.M21 = left.M21 + right.M21;
			result.M22 = left.M22 + right.M22;
			result.M23 = left.M23 + right.M23;
			result.M24 = left.M24 + right.M24;
			result.M31 = left.M31 + right.M31;
			result.M32 = left.M32 + right.M32;
			result.M33 = left.M33 + right.M33;
			result.M34 = left.M34 + right.M34;
			result.M41 = left.M41 + right.M41;
			result.M42 = left.M42 + right.M42;
			result.M43 = left.M43 + right.M43;
			result.M44 = left.M44 + right.M44;
			return result;
		}
		Matrix Matrix::operator - (Matrix left, Matrix right)
		{
			Matrix result;
			result.M11 = left.M11 - right.M11;
			result.M12 = left.M12 - right.M12;
			result.M13 = left.M13 - right.M13;
			result.M14 = left.M14 - right.M14;
			result.M21 = left.M21 - right.M21;
			result.M22 = left.M22 - right.M22;
			result.M23 = left.M23 - right.M23;
			result.M24 = left.M24 - right.M24;
			result.M31 = left.M31 - right.M31;
			result.M32 = left.M32 - right.M32;
			result.M33 = left.M33 - right.M33;
			result.M34 = left.M34 - right.M34;
			result.M41 = left.M41 - right.M41;
			result.M42 = left.M42 - right.M42;
			result.M43 = left.M43 - right.M43;
			result.M44 = left.M44 - right.M44;
			return result;
		}
		Matrix Matrix::operator - (Matrix matrix)
		{
			Matrix result;
			result.M11 = -matrix.M11;
			result.M12 = -matrix.M12;
			result.M13 = -matrix.M13;
			result.M14 = -matrix.M14;
			result.M21 = -matrix.M21;
			result.M22 = -matrix.M22;
			result.M23 = -matrix.M23;
			result.M24 = -matrix.M24;
			result.M31 = -matrix.M31;
			result.M32 = -matrix.M32;
			result.M33 = -matrix.M33;
			result.M34 = -matrix.M34;
			result.M41 = -matrix.M41;
			result.M42 = -matrix.M42;
			result.M43 = -matrix.M43;
			result.M44 = -matrix.M44;
			return result;
		}
		bool Matrix::operator == (Matrix left, Matrix right)
		{
			return Matrix::Equals(left, right);
		}
		bool Matrix::operator != (Matrix left, Matrix right)
		{
			return !Matrix::Equals(left, right);
		}

		array<float> ^Matrix::ToArray()
		{
			array<float> ^result = gcnew array<float>(16);
			result[0] = M11;
			result[1] = M12;
			result[2] = M13;
			result[3] = M14;
			result[4] = M21;
			result[5] = M22;
			result[6] = M23;
			result[7] = M24;
			result[8] = M31;
			result[9] = M32;
			result[10] = M33;
			result[11] = M34;
			result[12] = M41;
			result[13] = M42;
			result[14] = M43;
			result[15] = M44;

			return result;
		}
		String ^Matrix::ToString()
		{
			return String::Format(CultureInfo::CurrentCulture, "[[M11:{0} M12:{1} M13:{2} M14:{3}] [M21:{4} M22:{5} M23:{6} M24:{7}] [M31:{8} M32:{9} M33:{10} M34:{11}] [M41:{12} M42:{13} M43:{14} M44:{15}]]",
				M11.ToString(CultureInfo::CurrentCulture), M12.ToString(CultureInfo::CurrentCulture), M13.ToString(CultureInfo::CurrentCulture), M14.ToString(CultureInfo::CurrentCulture),
				M21.ToString(CultureInfo::CurrentCulture), M22.ToString(CultureInfo::CurrentCulture), M23.ToString(CultureInfo::CurrentCulture), M24.ToString(CultureInfo::CurrentCulture),
				M31.ToString(CultureInfo::CurrentCulture), M32.ToString(CultureInfo::CurrentCulture), M33.ToString(CultureInfo::CurrentCulture), M34.ToString(CultureInfo::CurrentCulture),
				M41.ToString(CultureInfo::CurrentCulture), M42.ToString(CultureInfo::CurrentCulture), M43.ToString(CultureInfo::CurrentCulture), M44.ToString(CultureInfo::CurrentCulture));
		}
		int Matrix::GetHashCode()
		{
			return M11.GetHashCode() + M12.GetHashCode() + M13.GetHashCode() + M14.GetHashCode() +
				M21.GetHashCode() + M22.GetHashCode() + M23.GetHashCode() + M24.GetHashCode() +
				M31.GetHashCode() + M32.GetHashCode() + M33.GetHashCode() + M34.GetHashCode() +
				M41.GetHashCode() + M42.GetHashCode() + M43.GetHashCode() + M44.GetHashCode();
		}
		bool Matrix::Equals(Object ^value)
		{
			if (value == nullptr || value->GetType() != GetType())
				return false;

			return Equals(safe_cast<Matrix>(value));
		}
		bool Matrix::Equals(Matrix value)
		{
			return (M11 == value.M11 && M12 == value.M12 && M13 == value.M13 && M14 == value.M14 &&
				M21 == value.M21 && M22 == value.M22 && M23 == value.M23 && M24 == value.M24 &&
				M31 == value.M31 && M32 == value.M32 && M33 == value.M33 && M34 == value.M34 &&
				M41 == value.M41 && M42 == value.M42 && M43 == value.M43 && M44 == value.M44);
		}
		bool Matrix::Equals(Matrix %value1, Matrix %value2)
		{
			return (value1.M11 == value2.M11 && value1.M12 == value2.M12 && value1.M13 == value2.M13 && value1.M14 == value2.M14 &&
				value1.M21 == value2.M21 && value1.M22 == value2.M22 && value1.M23 == value2.M23 && value1.M24 == value2.M24 &&
				value1.M31 == value2.M31 && value1.M32 == value2.M32 && value1.M33 == value2.M33 && value1.M34 == value2.M34 &&
				value1.M41 == value2.M41 && value1.M42 == value2.M42 && value1.M43 == value2.M43 && value1.M44 == value2.M44);
		}
	}
}